home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / non-ANSI / c-client / tenex2.c < prev    next >
C/C++ Source or Header  |  1996-10-15  |  61KB  |  2,065 lines

  1. /*
  2.  * Program:    Tenex mail routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    22 May 1990
  13.  * Last Edited:    15 October 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <stdio.h>
  38. #include <ctype.h>
  39. #include <errno.h>
  40. extern int errno;        /* just in case */
  41. #include "mail.h"
  42. #include "osdep.h"
  43. #include <pwd.h>
  44. #include <sys/stat.h>
  45. #include <sys/time.h>
  46. #include "tenex2.h"
  47. #include "rfc822.h"
  48. #include "misc.h"
  49. #include "dummy.h"
  50.  
  51. /* Tenex mail routines */
  52.  
  53.  
  54. /* Driver dispatch used by MAIL */
  55.  
  56. DRIVER tenexdriver = {
  57.   "tenex",            /* driver name */
  58.   (DRIVER *) NIL,        /* next driver */
  59.   tenex_valid,            /* mailbox is valid for us */
  60.   tenex_parameters,        /* manipulate parameters */
  61.   tenex_find,            /* find mailboxes */
  62.   tenex_find_bboards,        /* find bboards */
  63.   tenex_find_all,        /* find all mailboxes */
  64.   tenex_find_all_bboards,    /* find all bboards */
  65.   tenex_subscribe,        /* subscribe to mailbox */
  66.   tenex_unsubscribe,        /* unsubscribe from mailbox */
  67.   tenex_subscribe_bboard,    /* subscribe to bboard */
  68.   tenex_unsubscribe_bboard,    /* unsubscribe from bboard */
  69.   tenex_create,            /* create mailbox */
  70.   tenex_delete,            /* delete mailbox */
  71.   tenex_rename,            /* rename mailbox */
  72.   tenex_open,            /* open mailbox */
  73.   tenex_close,            /* close mailbox */
  74.   tenex_fetchfast,        /* fetch message "fast" attributes */
  75.   tenex_fetchflags,        /* fetch message flags */
  76.   tenex_fetchstructure,        /* fetch message envelopes */
  77.   tenex_fetchheader,        /* fetch message header only */
  78.   tenex_fetchtext,        /* fetch message body only */
  79.   tenex_fetchbody,        /* fetch message body section */
  80.   tenex_setflag,        /* set message flag */
  81.   tenex_clearflag,        /* clear message flag */
  82.   tenex_search,            /* search for message based on criteria */
  83.   tenex_ping,            /* ping mailbox to see if still alive */
  84.   tenex_check,            /* check for new messages */
  85.   tenex_expunge,        /* expunge deleted messages */
  86.   tenex_copy,            /* copy messages to another mailbox */
  87.   tenex_move,            /* move messages to another mailbox */
  88.   tenex_append,            /* append string message to mailbox */
  89.   tenex_gc            /* garbage collect stream */
  90. };
  91.  
  92.                 /* prototype stream */
  93. MAILSTREAM tenexproto = {&tenexdriver};
  94.  
  95. /* Tenex mail validate mailbox
  96.  * Accepts: mailbox name
  97.  * Returns: our driver if name is valid, NIL otherwise
  98.  */
  99.  
  100. DRIVER *tenex_valid (name)
  101.     char *name;
  102. {
  103.   char tmp[MAILTMPLEN];
  104.   return tenex_isvalid (name,tmp) ? &tenexdriver : NIL;
  105. }
  106.  
  107.  
  108. /* Tenex mail test for valid mailbox
  109.  * Accepts: mailbox name
  110.  * Returns: T if valid, NIL otherwise
  111.  */
  112.  
  113. int tenex_isvalid (name,tmp)
  114.     char *name;
  115.     char *tmp;
  116. {
  117.   int fd;
  118.   int ret = NIL;
  119.   char *s,file[MAILTMPLEN];
  120.   struct stat sbuf;
  121.   time_t tp[2];
  122.   errno = EINVAL;        /* assume invalid argument */
  123.                 /* if file, get its status */
  124.   if ((*name != '{') && !((*name == '*') && (name[1] == '{')) &&
  125.       (s = tenex_file (file,name)) && !stat (s,&sbuf)) {
  126.     if (!sbuf.st_size) {    /* allow empty file if INBOX */
  127.       if ((s = mailboxfile (tmp,name)) && !*s) ret = T;
  128.       else errno = 0;        /* empty file */
  129.     }
  130.     else if ((fd = open (file,O_RDONLY,NIL)) >= 0) {
  131.       memset (tmp,'\0',MAILTMPLEN);
  132.       if ((read (fd,tmp,64) >= 0) && (s = strchr (tmp,'\012')) &&
  133.       (s[-1] != '\015')) {    /* valid format? */
  134.     *s = '\0';        /* tie off header */
  135.                 /* must begin with dd-mmm-yy" */
  136.     ret = (((tmp[2] == '-' && tmp[6] == '-') ||
  137.         (tmp[1] == '-' && tmp[5] == '-')) &&
  138.            (s = strchr (tmp+20,',')) && strchr (s+2,';')) ? T : NIL;
  139.       }
  140.       else errno = -1;        /* bogus format */
  141.       close (fd);        /* close the file */
  142.       tp[0] = sbuf.st_atime;    /* preserve atime and mtime */
  143.       tp[1] = sbuf.st_mtime;
  144.       utime (file,tp);        /* set the times */
  145.     }
  146.   }
  147.   return ret;            /* return what we should */
  148. }
  149.  
  150.  
  151. /* Tenex manipulate driver parameters
  152.  * Accepts: function code
  153.  *        function-dependent value
  154.  * Returns: function-dependent return value
  155.  */
  156.  
  157. void *tenex_parameters (function,value)
  158.     long function;
  159.     void *value;
  160. {
  161.   return NIL;
  162. }
  163.  
  164. /* Tenex mail find list of mailboxes
  165.  * Accepts: mail stream
  166.  *        pattern to search
  167.  */
  168.  
  169. void tenex_find (stream,pat)
  170.     MAILSTREAM *stream;
  171.     char *pat;
  172. {
  173.   if (stream) dummy_find (NIL,pat);
  174. }
  175.  
  176.  
  177. /* Tenex mail find list of bboards
  178.  * Accepts: mail stream
  179.  *        pattern to search
  180.  */
  181.  
  182. void tenex_find_bboards (stream,pat)
  183.     MAILSTREAM *stream;
  184.     char *pat;
  185. {
  186.   if (stream) dummy_find_bboards (NIL,pat);
  187. }
  188.  
  189.  
  190. /* Tenex mail find list of all mailboxes
  191.  * Accepts: mail stream
  192.  *        pattern to search
  193.  */
  194.  
  195. void tenex_find_all (stream,pat)
  196.     MAILSTREAM *stream;
  197.     char *pat;
  198. {
  199.   if (stream) dummy_find_all (NIL,pat);
  200. }
  201.  
  202.  
  203. /* Tenex mail find list of all bboards
  204.  * Accepts: mail stream
  205.  *        pattern to search
  206.  */
  207.  
  208. void tenex_find_all_bboards (stream,pat)
  209.     MAILSTREAM *stream;
  210.     char *pat;
  211. {
  212.   if (stream) dummy_find_all_bboards (NIL,pat);
  213. }
  214.  
  215. /* Tenex mail subscribe to mailbox
  216.  * Accepts: mail stream
  217.  *        mailbox to add to subscription list
  218.  * Returns: T on success, NIL on failure
  219.  */
  220.  
  221. long tenex_subscribe (stream,mailbox)
  222.     MAILSTREAM *stream;
  223.     char *mailbox;
  224. {
  225.   char tmp[MAILTMPLEN];
  226.   return sm_subscribe (tenex_file (tmp,mailbox));
  227. }
  228.  
  229.  
  230. /* Tenex mail unsubscribe to mailbox
  231.  * Accepts: mail stream
  232.  *        mailbox to delete from subscription list
  233.  * Returns: T on success, NIL on failure
  234.  */
  235.  
  236. long tenex_unsubscribe (stream,mailbox)
  237.     MAILSTREAM *stream;
  238.     char *mailbox;
  239. {
  240.   char tmp[MAILTMPLEN];
  241.   return sm_unsubscribe (tenex_file (tmp,mailbox));
  242. }
  243.  
  244.  
  245. /* Tenex mail subscribe to bboard
  246.  * Accepts: mail stream
  247.  *        bboard to add to subscription list
  248.  * Returns: T on success, NIL on failure
  249.  */
  250.  
  251. long tenex_subscribe_bboard (stream,mailbox)
  252.     MAILSTREAM *stream;
  253.     char *mailbox;
  254. {
  255.   return NIL;            /* never valid for Tenex */
  256. }
  257.  
  258.  
  259. /* Tenex mail unsubscribe to bboard
  260.  * Accepts: mail stream
  261.  *        bboard to delete from subscription list
  262.  * Returns: T on success, NIL on failure
  263.  */
  264.  
  265. long tenex_unsubscribe_bboard (stream,mailbox)
  266.     MAILSTREAM *stream;
  267.     char *mailbox;
  268. {
  269.   return NIL;            /* never valid for Tenex */
  270. }
  271.  
  272. /* Tenex mail create mailbox
  273.  * Accepts: MAIL stream
  274.  *        mailbox name to create
  275.  * Returns: T on success, NIL on failure
  276.  */
  277.  
  278. long tenex_create (stream,mailbox)
  279.     MAILSTREAM *stream;
  280.     char *mailbox;
  281. {
  282.   return dummy_create (stream,mailbox);
  283. }
  284.  
  285.  
  286. /* Tenex mail delete mailbox
  287.  * Accepts: MAIL stream
  288.  *        mailbox name to delete
  289.  * Returns: T on success, NIL on failure
  290.  */
  291.  
  292. long tenex_delete (stream,mailbox)
  293.     MAILSTREAM *stream;
  294.     char *mailbox;
  295. {
  296.   return tenex_rename (stream,mailbox,NIL);
  297. }
  298.  
  299. /* Tenex mail rename mailbox
  300.  * Accepts: MAIL stream
  301.  *        old mailbox name
  302.  *        new mailbox name (or NIL for delete)
  303.  * Returns: T on success, NIL on failure
  304.  */
  305.  
  306. long tenex_rename (stream,old,new)
  307.     MAILSTREAM *stream;
  308.     char *old;
  309.     char *new;
  310. {
  311.   long ret = T;
  312.   char tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
  313.   int ld;
  314.   int fd = open (tenex_file (file,old),O_RDWR,NIL);
  315.                 /* lock out non c-client applications */
  316.   if (fd < 0) {            /* open mailbox */
  317.     sprintf (tmp,"Can't open mailbox %s: %s",old,strerror (errno));
  318.     mm_log (tmp,ERROR);
  319.     return NIL;
  320.   }
  321.                 /* get exclusive parse/append permission */
  322.   if ((ld = tenex_lock (fd,lock,LOCK_EX)) < 0) {
  323.     mm_log ("Unable to lock rename mailbox",ERROR);
  324.     return NIL;
  325.   }
  326.                 /* lock out other users */
  327.   if (flock (fd,LOCK_EX|LOCK_NB)) {
  328.     close (fd);            /* couldn't lock, give up on it then */
  329.     sprintf (tmp,"Mailbox %s is in use by another process",old);
  330.     mm_log (tmp,ERROR);
  331.     tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  332.     return NIL;
  333.   }
  334.                 /* do the rename or delete operation */
  335.   if (new ? rename (file,tenex_file (tmp,new)) : unlink (file)) {
  336.     sprintf (tmp,"Can't %s mailbox %s: %s",new ? "rename" : "delete",old,
  337.          strerror (errno));
  338.     mm_log (tmp,ERROR);
  339.     ret = NIL;            /* set failure */
  340.   }
  341.   flock (fd,LOCK_UN);        /* release lock on the file */
  342.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  343.   close (fd);            /* close the file */
  344.   return ret;            /* return success */
  345. }
  346.  
  347. /* Tenex mail open
  348.  * Accepts: stream to open
  349.  * Returns: stream on success, NIL on failure
  350.  */
  351.  
  352. MAILSTREAM *tenex_open (stream)
  353.     MAILSTREAM *stream;
  354. {
  355.   int fd,ld;
  356.   char tmp[MAILTMPLEN];
  357.                 /* return prototype for OP_PROTOTYPE call */
  358.   if (!stream) return user_flags (&tenexproto);
  359.   if (LOCAL) {            /* close old file if stream being recycled */
  360.     tenex_close (stream);    /* dump and save the changes */
  361.     stream->dtb = &tenexdriver;    /* reattach this driver */
  362.     mail_free_cache (stream);    /* clean up cache */
  363.   }
  364.   user_flags (stream);        /* set up user flags */
  365.                 /* force readonly if bboard */
  366.   if (*stream->mailbox == '*') stream->rdonly = T;
  367.   if (stream->rdonly ||
  368.       (fd = open (tenex_file (tmp,stream->mailbox),O_RDWR,NIL)) < 0) {
  369.     if ((fd = open (tenex_file (tmp,stream->mailbox),O_RDONLY,NIL)) < 0) {
  370.       sprintf (tmp,"Can't open mailbox: %s",strerror (errno));
  371.       mm_log (tmp,ERROR);
  372.       return NIL;
  373.     }
  374.     else if (!stream->rdonly) {    /* got it, but readonly */
  375.       mm_log ("Can't get write access to mailbox, access is readonly",WARN);
  376.       stream->rdonly = T;
  377.     }
  378.   }
  379.   stream->local = fs_get (sizeof (TENEXLOCAL));
  380.   LOCAL->buf = (char *) fs_get (MAXMESSAGESIZE + 1);
  381.   LOCAL->buflen = MAXMESSAGESIZE;
  382.   LOCAL->hdr = LOCAL->txt = NIL;/* no cached data yet */
  383.   LOCAL->hdrmsgno = LOCAL->txtmsgno = 0;
  384.                 /* note if an INBOX or not */
  385.   LOCAL->inbox = !strcmp (ucase (strcpy (LOCAL->buf,stream->mailbox)),"INBOX");
  386.   if (*stream->mailbox != '*') {/* canonicalize the stream mailbox name */
  387.     fs_give ((void **) &stream->mailbox);
  388.     stream->mailbox = cpystr (tmp);
  389.   }
  390.                 /* get shared parse permission */
  391.   if ((ld = tenex_lock (fd,tmp,LOCK_SH)) < 0) {
  392.     mm_log ("Unable to lock open mailbox",ERROR);
  393.     return NIL;
  394.   }
  395.   flock(LOCAL->fd = fd,LOCK_SH);/* bind and lock the file */
  396.   tenex_unlock (ld,tmp);    /* release shared parse permission */
  397.   LOCAL->filesize = 0;        /* initialize parsed file size */
  398.   LOCAL->filetime = 0;        /* time not set up yet */
  399.   LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
  400.   stream->sequence++;        /* bump sequence number */
  401.                 /* parse mailbox */
  402.   stream->nmsgs = stream->recent = 0;
  403.   if (tenex_ping (stream) && !stream->nmsgs)
  404.     mm_log ("Mailbox is empty",(long) NIL);
  405.   if (!LOCAL) return NIL;    /* failure if stream died */
  406.   return stream;        /* return stream to caller */
  407. }
  408.  
  409. /* Tenex mail close
  410.  * Accepts: MAIL stream
  411.  */
  412.  
  413. void tenex_close (stream)
  414.     MAILSTREAM *stream;
  415. {
  416.   if (stream && LOCAL) {    /* only if a file is open */
  417.     flock (LOCAL->fd,LOCK_UN);    /* unlock local file */
  418.     close (LOCAL->fd);        /* close the local file */
  419.                 /* free local text buffer */
  420.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  421.     if (LOCAL->hdr) fs_give ((void **) &LOCAL->hdr);
  422.     if (LOCAL->txt) fs_give ((void **) &LOCAL->txt);
  423.                 /* nuke the local data */
  424.     fs_give ((void **) &stream->local);
  425.     stream->dtb = NIL;        /* log out the DTB */
  426.   }
  427. }
  428.  
  429.  
  430. /* Tenex mail fetch fast information
  431.  * Accepts: MAIL stream
  432.  *        sequence
  433.  */
  434.  
  435. void tenex_fetchfast (stream,sequence)
  436.     MAILSTREAM *stream;
  437.     char *sequence;
  438. {
  439.   long i;
  440.                 /* make sure have RFC-822 size for messages */
  441.   if (stream && LOCAL && mail_sequence (stream,sequence))
  442.     for (i = 1; i <= stream->nmsgs; i++)
  443.       if (mail_elt (stream,i)->sequence) tenex_822size (stream,i);
  444. }
  445.  
  446.  
  447. /* Tenex mail fetch flags
  448.  * Accepts: MAIL stream
  449.  *        sequence
  450.  */
  451.  
  452. void tenex_fetchflags (stream,sequence)
  453.     MAILSTREAM *stream;
  454.     char *sequence;
  455. {
  456.   long i;
  457.                 /* ping mailbox, get new status for messages */
  458.   if (tenex_ping (stream) && mail_sequence (stream,sequence))
  459.     for (i = 1; i <= stream->nmsgs; i++)
  460.       if (mail_elt (stream,i)->sequence) tenex_elt (stream,i);
  461. }
  462.  
  463. /* Tenex mail fetch structure
  464.  * Accepts: MAIL stream
  465.  *        message # to fetch
  466.  *        pointer to return body
  467.  * Returns: envelope of this message, body returned in body value
  468.  *
  469.  * Fetches the "fast" information as well
  470.  */
  471.  
  472. ENVELOPE *tenex_fetchstructure (stream,msgno,body)
  473.     MAILSTREAM *stream;
  474.     long msgno;
  475.     BODY **body;
  476. {
  477.   LONGCACHE *lelt;
  478.   ENVELOPE **env;
  479.   BODY **b;
  480.   STRING bs;
  481.   unsigned long i,hdrsize,textsize;
  482.   tenex_822size (stream,msgno);    /* make sure we have message size */
  483.   if (stream->scache) {        /* short cache */
  484.     if (msgno != stream->msgno){/* flush old poop if a different message */
  485.       mail_free_envelope (&stream->env);
  486.       mail_free_body (&stream->body);
  487.     }
  488.     stream->msgno = msgno;
  489.     env = &stream->env;        /* get pointers to envelope and body */
  490.     b = &stream->body;
  491.   }
  492.   else {            /* long cache */
  493.     lelt = mail_lelt (stream,msgno);
  494.     env = &lelt->env;        /* get pointers to envelope and body */
  495.     b = &lelt->body;
  496.   }
  497.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  498.     mail_free_envelope (env);    /* flush old envelope and body */
  499.     mail_free_body (b);
  500.                 /* read the header */
  501.     tenex_fetchheader_work (stream,msgno,&hdrsize);
  502.     if (body) {            /* don't bother with text if don't want body */
  503.       tenex_fetchtext_work (stream,msgno,&textsize);
  504.       INIT (&bs,mail_string,(void *) LOCAL->txt,textsize);
  505.     }
  506.     else textsize = 0;        /* no text then */
  507.                 /* make sure enough space */
  508.     if ((i = max (hdrsize,textsize)) > LOCAL->buflen) {
  509.       fs_give ((void **) &LOCAL->buf);
  510.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = i) + 1);
  511.     }
  512.                 /* parse envelope and body */
  513.     rfc822_parse_msg (env,body ? b : NIL,LOCAL->hdr,hdrsize,body ? &bs : NIL,
  514.               mylocalhost (),LOCAL->buf);
  515.   }
  516.   if (body) *body = *b;        /* return the body */
  517.   return *env;            /* return the envelope */
  518. }
  519.  
  520. /* Tenex mail fetch message header
  521.  * Accepts: MAIL stream
  522.  *        message # to fetch
  523.  * Returns: message header in RFC822 format
  524.  */
  525.  
  526. char *tenex_fetchheader (stream,msgno)
  527.     MAILSTREAM *stream;
  528.     long msgno;
  529. {
  530.   unsigned long hdrsize;
  531.   char *hdr = tenex_fetchheader_work (stream,msgno,&hdrsize);
  532.                 /* copy the string */
  533.   strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,hdr,hdrsize);
  534.   return LOCAL->buf;
  535. }
  536.  
  537.  
  538. /* Tenex mail fetch message header work
  539.  * Accepts: MAIL stream
  540.  *        message # to fetch
  541.  *        pointer to size to return
  542.  * Returns: message header in RFC822 format
  543.  */
  544.  
  545. char *tenex_fetchheader_work (stream,m,siz)
  546.     MAILSTREAM *stream;
  547.     long m;
  548.     unsigned long *siz;
  549. {
  550.   unsigned long pos = tenex_header (stream,m,siz);
  551.   if (LOCAL->hdrmsgno != m) {    /* is cache correct? */
  552.     if (LOCAL->hdr) fs_give ((void **) &LOCAL->hdr);
  553.     LOCAL->hdr = (char *) fs_get (1 + *siz);
  554.     LOCAL->hdr[*siz] = '\0';    /* tie off string */
  555.     LOCAL->hdrmsgno = m;    /* note cache */
  556.     lseek (LOCAL->fd,pos,L_SET);/* get to header position */
  557.                 /* slurp the data */
  558.     read (LOCAL->fd,LOCAL->hdr,*siz);
  559.   }
  560.   return LOCAL->hdr;
  561. }
  562.  
  563. /* Tenex mail fetch message text (only)
  564.     body only;
  565.  * Accepts: MAIL stream
  566.  *        message # to fetch
  567.  * Returns: message text in RFC822 format
  568.  */
  569.  
  570. char *tenex_fetchtext (stream,msgno)
  571.     MAILSTREAM *stream;
  572.     long msgno;
  573. {
  574.   unsigned long txtsize;
  575.   char *txt = tenex_fetchtext_work (stream,msgno,&txtsize);
  576.                 /* mark message as seen */
  577.   tenex_elt (stream,msgno)->seen = T;
  578.                 /* recalculate status */
  579.   tenex_update_status (stream,msgno,T);
  580.                 /* copy the string */
  581.   strcrlfcpy (&LOCAL->buf,&LOCAL->buflen,txt,txtsize);
  582.   return LOCAL->buf;
  583. }
  584.  
  585.  
  586. /* Tenex mail fetch message header work
  587.  * Accepts: MAIL stream
  588.  *        message # to fetch
  589.  *        pointer to size to return
  590.  * Returns: message header in RFC822 format
  591.  */
  592.  
  593. char *tenex_fetchtext_work (stream,m,siz)
  594.     MAILSTREAM *stream;
  595.     long m;
  596.     unsigned long *siz;
  597. {
  598.   unsigned long hdrsize;
  599.   unsigned long pos = tenex_header (stream,m,&hdrsize);
  600.   *siz = tenex_size (stream,m) - hdrsize;
  601.   if (LOCAL->txtmsgno != m) {    /* is cache correct? */
  602.     if (LOCAL->txt) fs_give ((void **) &LOCAL->txt);
  603.     LOCAL->txt = (char *) fs_get (1 + *siz);
  604.     LOCAL->txt[*siz] = '\0';    /* tie off string */
  605.     LOCAL->txtmsgno = m;    /* note cache */
  606.                 /* get to text position */
  607.     lseek (LOCAL->fd,pos + hdrsize,L_SET);
  608.                 /* slurp the data */
  609.     read (LOCAL->fd,LOCAL->txt,*siz);
  610.   }
  611.   return LOCAL->txt;
  612. }
  613.  
  614. /* Tenex fetch message body as a structure
  615.  * Accepts: Mail stream
  616.  *        message # to fetch
  617.  *        section specifier
  618.  *        pointer to length
  619.  * Returns: pointer to section of message body
  620.  */
  621.  
  622. char *tenex_fetchbody (stream,m,s,len)
  623.     MAILSTREAM *stream;
  624.     long m;
  625.     char *s;
  626.     unsigned long *len;
  627. {
  628.   BODY *b;
  629.   PART *pt;
  630.   char *t;
  631.   unsigned long i;
  632.   unsigned long base;
  633.   unsigned long offset = 0;
  634.   unsigned long hdrpos = tenex_header (stream,m,&base);
  635.   MESSAGECACHE *elt = tenex_elt (stream,m);
  636.                 /* make sure have a body */
  637.   if (!(tenex_fetchstructure (stream,m,&b) && b && s && *s &&
  638.     ((i = strtol (s,&s,10)) > 0))) return NIL;
  639.   do {                /* until find desired body part */
  640.                 /* multipart content? */
  641.     if (b->type == TYPEMULTIPART) {
  642.       pt = b->contents.part;    /* yes, find desired part */
  643.       while (--i && (pt = pt->next));
  644.       if (!pt) return NIL;    /* bad specifier */
  645.                 /* note new body, check valid nesting */
  646.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  647.       offset = pt->offset;    /* get new offset */
  648.     }
  649.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  650.                 /* need to go down further? */
  651.     if (i = *s) switch (b->type) {
  652.     case TYPEMESSAGE:        /* embedded message, calculate new base */
  653.       offset = b->contents.msg.offset;
  654.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  655.     case TYPEMULTIPART:        /* multipart, get next section */
  656.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  657.     default:            /* bogus subpart specification */
  658.       return NIL;
  659.     }
  660.   } while (i);
  661.                 /* lose if body bogus */
  662.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  663.   elt->seen = T;        /* mark message as seen */
  664.                 /* recalculate status */
  665.   tenex_update_status (stream,m,T);
  666.                 /* move to that place in the data */
  667.   lseek (LOCAL->fd,hdrpos + base + offset,L_SET);
  668.                 /* slurp the data */
  669.   t = (char *) fs_get (1 + b->size.ibytes);
  670.   read (LOCAL->fd,t,b->size.ibytes);
  671.   rfc822_contents(&LOCAL->buf,&LOCAL->buflen,len,t,b->size.ibytes,b->encoding);
  672.   fs_give ((void **) &t);    /* flush readin buffer */
  673.   return LOCAL->buf;
  674. }
  675.  
  676. /* Tenex locate header for a message
  677.  * Accepts: MAIL stream
  678.  *        message number
  679.  *        pointer to returned header size
  680.  * Returns: position of header in file
  681.  */
  682.  
  683. unsigned long tenex_header (stream,msgno,size)
  684.     MAILSTREAM *stream;
  685.     long msgno;
  686.     unsigned long *size;
  687. {
  688.   long siz;
  689.   long i = 0;
  690.   char c = '\0';
  691.   char *s = NIL;
  692.   MESSAGECACHE *elt = tenex_elt (stream,msgno);
  693.   long pos = elt->data1 + (elt->data2 >> 24);
  694.   long msiz = tenex_size (stream,msgno);
  695.                 /* is size known? */
  696.   if (!(*size = (elt->data2 & (unsigned long) 0xffffff))) {
  697.     lseek (LOCAL->fd,pos,L_SET);/* get to header position */
  698.                 /* search message for LF LF */
  699.     for (siz = 0; siz < msiz; siz++) {
  700.       if (--i <= 0)        /* read another buffer as necessary */
  701.     read (LOCAL->fd,s = LOCAL->buf,i = min (msiz-siz,(long) MAILTMPLEN));
  702.                 /* two newline sequence? */
  703.       if ((c == '\012') && (*s == '\012')) {
  704.                 /* yes, note for later */
  705.     elt->data2 |= (*size = siz + 1);
  706.     return pos;        /* return to caller */
  707.       }
  708.       else c = *s++;        /* next character */
  709.     }
  710.   }
  711.   return pos;            /* have position */
  712. }
  713.  
  714. /* Tenex mail set flag
  715.  * Accepts: MAIL stream
  716.  *        sequence
  717.  *        flag(s)
  718.  */
  719.  
  720. void tenex_setflag (stream,sequence,flag)
  721.     MAILSTREAM *stream;
  722.     char *sequence;
  723.     char *flag;
  724. {
  725.   MESSAGECACHE *elt;
  726.   long i;
  727.   unsigned long uf;
  728.   short f;
  729.   struct stat sbuf;
  730.   if (LOCAL->filetime && !LOCAL->shouldcheck) {
  731.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  732.     if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
  733.   }
  734.                 /* no-op if no flags to modify */
  735.   if (!((f = tenex_getflags (stream,flag,&uf)) || uf)) return;
  736.                 /* get sequence and loop on it */
  737.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  738.     if ((elt = mail_elt (stream,i))->sequence) {
  739.       tenex_elt (stream,i);    /* set all requested flags */
  740.       if (f&fSEEN) elt->seen = T;
  741.       if (f&fDELETED) elt->deleted = T;
  742.       if (f&fFLAGGED) elt->flagged = T;
  743.       if (f&fANSWERED) elt->answered = T;
  744.       elt->user_flags |= uf;
  745.                 /* recalculate status */
  746.       tenex_update_status (stream,i,NIL);
  747.     }
  748.   if (!stream->rdonly) {    /* make sure the update takes */
  749.     fsync (LOCAL->fd);
  750.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  751.     LOCAL->filetime = sbuf.st_mtime;
  752.   }
  753. }
  754.  
  755. /* Tenex mail clear flag
  756.  * Accepts: MAIL stream
  757.  *        sequence
  758.  *        flag(s)
  759.  */
  760.  
  761. void tenex_clearflag (stream,sequence,flag)
  762.     MAILSTREAM *stream;
  763.     char *sequence;
  764.     char *flag;
  765. {
  766.   MESSAGECACHE *elt;
  767.   long i;
  768.   unsigned long uf;
  769.   short f;
  770.   struct stat sbuf;
  771.   if (LOCAL->filetime && !LOCAL->shouldcheck) {
  772.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  773.     if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
  774.   }
  775.                 /* no-op if no flags to modify */
  776.   if (!((f = tenex_getflags (stream,flag,&uf)) || uf)) return;
  777.                 /* get sequence and loop on it */
  778.   if (mail_sequence (stream,sequence)) for (i = 1; i <= stream->nmsgs; i++)
  779.     if ((elt = mail_elt (stream,i))->sequence) {
  780.       tenex_elt (stream,i);    /* clear all requested flags */
  781.       if (f&fSEEN) elt->seen = NIL;
  782.       if (f&fDELETED) elt->deleted = NIL;
  783.       if (f&fFLAGGED) elt->flagged = NIL;
  784.       if (f&fANSWERED) elt->answered = NIL;
  785.       elt->user_flags &= ~uf;
  786.                 /* recalculate status */
  787.       tenex_update_status (stream,i,NIL);
  788.     }
  789.   if (!stream->rdonly) {    /* make sure the update takes */
  790.     fsync (LOCAL->fd);
  791.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  792.     LOCAL->filetime = sbuf.st_mtime;
  793.   }
  794. }
  795.  
  796. /* Tenex mail search for messages
  797.  * Accepts: MAIL stream
  798.  *        search criteria
  799.  */
  800.  
  801. void tenex_search (stream,criteria)
  802.     MAILSTREAM *stream;
  803.     char *criteria;
  804. {
  805.   long i,n;
  806.   char *d;
  807.   search_t f;
  808.                 /* initially all searched */
  809.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  810.                 /* get first criterion */
  811.   if (criteria && (criteria = strtok (criteria," "))) {
  812.                 /* for each criterion */
  813.     for (; criteria; (criteria = strtok (NIL," "))) {
  814.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  815.       switch (*ucase (criteria)) {
  816.       case 'A':            /* possible ALL, ANSWERED */
  817.     if (!strcmp (criteria+1,"LL")) f = tenex_search_all;
  818.     else if (!strcmp (criteria+1,"NSWERED")) f = tenex_search_answered;
  819.     break;
  820.       case 'B':            /* possible BCC, BEFORE, BODY */
  821.     if (!strcmp (criteria+1,"CC"))
  822.       f = tenex_search_string (tenex_search_bcc,&d,&n);
  823.     else if (!strcmp (criteria+1,"EFORE"))
  824.       f = tenex_search_date (tenex_search_before,&n);
  825.     else if (!strcmp (criteria+1,"ODY"))
  826.       f = tenex_search_string (tenex_search_body,&d,&n);
  827.     break;
  828.       case 'C':            /* possible CC */
  829.     if (!strcmp (criteria+1,"C"))
  830.       f = tenex_search_string (tenex_search_cc,&d,&n);
  831.     break;
  832.       case 'D':            /* possible DELETED */
  833.     if (!strcmp (criteria+1,"ELETED")) f = tenex_search_deleted;
  834.     break;
  835.       case 'F':            /* possible FLAGGED, FROM */
  836.     if (!strcmp (criteria+1,"LAGGED")) f = tenex_search_flagged;
  837.     else if (!strcmp (criteria+1,"ROM"))
  838.       f = tenex_search_string (tenex_search_from,&d,&n);
  839.     break;
  840.       case 'K':            /* possible KEYWORD */
  841.     if (!strcmp (criteria+1,"EYWORD"))
  842.       f = tenex_search_flag (tenex_search_keyword,&n,stream);
  843.     break;
  844.       case 'N':            /* possible NEW */
  845.     if (!strcmp (criteria+1,"EW")) f = tenex_search_new;
  846.     break;
  847.  
  848.       case 'O':            /* possible OLD, ON */
  849.     if (!strcmp (criteria+1,"LD")) f = tenex_search_old;
  850.     else if (!strcmp (criteria+1,"N"))
  851.       f = tenex_search_date (tenex_search_on,&n);
  852.     break;
  853.       case 'R':            /* possible RECENT */
  854.     if (!strcmp (criteria+1,"ECENT")) f = tenex_search_recent;
  855.     break;
  856.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  857.     if (!strcmp (criteria+1,"EEN")) f = tenex_search_seen;
  858.     else if (!strcmp (criteria+1,"INCE"))
  859.       f = tenex_search_date (tenex_search_since,&n);
  860.     else if (!strcmp (criteria+1,"UBJECT"))
  861.       f = tenex_search_string (tenex_search_subject,&d,&n);
  862.     break;
  863.       case 'T':            /* possible TEXT, TO */
  864.     if (!strcmp (criteria+1,"EXT"))
  865.       f = tenex_search_string (tenex_search_text,&d,&n);
  866.     else if (!strcmp (criteria+1,"O"))
  867.       f = tenex_search_string (tenex_search_to,&d,&n);
  868.     break;
  869.       case 'U':            /* possible UN* */
  870.     if (criteria[1] == 'N') {
  871.       if (!strcmp (criteria+2,"ANSWERED")) f = tenex_search_unanswered;
  872.       else if (!strcmp (criteria+2,"DELETED")) f = tenex_search_undeleted;
  873.       else if (!strcmp (criteria+2,"FLAGGED")) f = tenex_search_unflagged;
  874.       else if (!strcmp (criteria+2,"KEYWORD"))
  875.         f = tenex_search_flag (tenex_search_unkeyword,&n,stream);
  876.       else if (!strcmp (criteria+2,"SEEN")) f = tenex_search_unseen;
  877.     }
  878.     break;
  879.       default:            /* we will barf below */
  880.     break;
  881.       }
  882.       if (!f) {            /* if can't determine any criteria */
  883.     sprintf (LOCAL->buf,"Unknown search criterion: %.80s",criteria);
  884.     mm_log (LOCAL->buf,ERROR);
  885.     return;
  886.       }
  887.                 /* run the search criterion */
  888.       for (i = 1; i <= stream->nmsgs; ++i)
  889.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  890.       mail_elt (stream,i)->searched = NIL;
  891.     }
  892.                 /* report search results to main program */
  893.     for (i = 1; i <= stream->nmsgs; ++i)
  894.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  895.   }
  896. }
  897.  
  898. /* Tenex mail ping mailbox
  899.  * Accepts: MAIL stream
  900.  * Returns: T if stream still alive, NIL if not
  901.  */
  902.  
  903. long tenex_ping (stream)
  904.     MAILSTREAM *stream;
  905. {
  906.   long i = 1;
  907.   long r = NIL;
  908.   int ld;
  909.   char lock[MAILTMPLEN];
  910.   struct stat sbuf;
  911.   if (stream && LOCAL) {    /* only if stream already open */
  912.     if (LOCAL->filetime && !(LOCAL->mustcheck || LOCAL->shouldcheck)) {
  913.       fstat (LOCAL->fd,&sbuf);    /* get current write time */
  914.       if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
  915.     }
  916.                 /* check for changed message status */
  917.     if (LOCAL->mustcheck || LOCAL->shouldcheck) {
  918.       if (LOCAL->shouldcheck)    /* babble when we do this unilaterally */
  919.     mm_notify (stream,"[CHECK] Checking for flag updates",NIL);
  920.       while (i <= stream->nmsgs) tenex_elt (stream,i++);
  921.       LOCAL->mustcheck = LOCAL->shouldcheck = NIL;
  922.     }
  923.                 /* get shared parse/append permission */
  924.     if ((ld = tenex_lock (LOCAL->fd,lock,LOCK_SH)) >= 0) {
  925.                 /* parse resulting mailbox */
  926.       r = (tenex_parse (stream)) ? T : NIL;
  927.       tenex_unlock (ld,lock);    /* release shared parse/append permission */
  928.     }
  929.                 /* snarf if this is a read-write inbox */
  930.     if (stream && LOCAL && LOCAL->inbox && !stream->rdonly) {
  931.       tenex_snarf (stream);
  932.                 /* get shared parse/append permission */
  933.       if ((ld = tenex_lock (LOCAL->fd,lock,LOCK_SH)) >= 0) {
  934.                 /* parse resulting mailbox */
  935.     r = (tenex_parse (stream)) ? T : NIL;
  936.     tenex_unlock (ld,lock);    /* release shared parse/append permission */
  937.       }
  938.     }
  939.   }
  940.   return r;            /* return result of the parse */
  941. }
  942.  
  943.  
  944. /* Tenex mail check mailbox (too)
  945.     reparses status too;
  946.  * Accepts: MAIL stream
  947.  */
  948.  
  949. void tenex_check (stream)
  950.     MAILSTREAM *stream;
  951. {
  952.                 /* mark that a check is desired */
  953.   if (LOCAL) LOCAL->mustcheck = T;
  954.   if (tenex_ping (stream)) mm_log ("Check completed",(long) NIL);
  955. }
  956.  
  957. /* Tenex mail snarf messages from bezerk inbox
  958.  * Accepts: MAIL stream
  959.  */
  960.  
  961. void tenex_snarf (stream)
  962.     MAILSTREAM *stream;
  963. {
  964.   long i = 0;
  965.   long r,j;
  966.   struct stat sbuf;
  967.   struct iovec iov[2];
  968.   char lock[MAILTMPLEN];
  969.   MESSAGECACHE *elt;
  970.   MAILSTREAM *bezerk = NIL;
  971.   int ld;
  972.                 /* give up if can't get exclusive permission */
  973.   if ((!strcmp (sysinbox (),stream->mailbox)) ||
  974.       ((ld = tenex_lock (LOCAL->fd,lock,LOCK_EX)) < 0)) return;
  975.   mm_critical (stream);        /* go critical */
  976.   stat (sysinbox (),&sbuf);    /* see if anything there */
  977.   if (sbuf.st_size) {        /* non-empty? */
  978.     fstat (LOCAL->fd,&sbuf);    /* yes, get current file size */
  979.                 /* sizes match and can get bezerk mailbox? */
  980.     if ((sbuf.st_size == LOCAL->filesize) &&
  981.     (bezerk = mail_open (bezerk,sysinbox (),OP_SILENT)) &&
  982.     (!bezerk->rdonly) && (r = bezerk->nmsgs)) {
  983.                 /* yes, go to end of file in our mailbox */
  984.       lseek (LOCAL->fd,sbuf.st_size,L_SET);
  985.                 /* for each message in bezerk mailbox */
  986.       while (r && (++i <= bezerk->nmsgs)) {
  987.                 /* snarf message from Berkeley mailbox */
  988.     iov[1].iov_base = bezerk_snarf (bezerk,i,&j);
  989.                 /* calculate header line */
  990.     mail_date ((iov[0].iov_base = LOCAL->buf),elt = mail_elt (bezerk,i));
  991.     sprintf (LOCAL->buf + strlen (LOCAL->buf),",%ld;0000000000%02o\n",
  992.          iov[1].iov_len = j,(fOLD * !(elt->recent)) +
  993.          (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  994.          (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered));
  995.     iov[0].iov_len = strlen (iov[0].iov_base);
  996.                 /* copy message to new mailbox */
  997.     if ((writev (LOCAL->fd,iov,2) < 0) || fsync (LOCAL->fd)) {
  998.       sprintf (LOCAL->buf,"Can't copy new mail: %s",strerror (errno));
  999.       mm_log (LOCAL->buf,ERROR);
  1000.       ftruncate (LOCAL->fd,sbuf.st_size);
  1001.       r = 0;        /* flag that we have lost big */
  1002.     }
  1003.       }
  1004.       fstat (LOCAL->fd,&sbuf);    /* yes, get current file size */
  1005.       LOCAL->filetime = sbuf.st_mtime;
  1006.       if (r) {            /* delete all the messages we copied */
  1007.     for (i = 1; i <= r; i++) mail_elt (bezerk,i)->deleted = T;
  1008.     mail_expunge (bezerk);    /* now expunge all those messages */
  1009.       }
  1010.     }
  1011.     if (bezerk) mail_close (bezerk);
  1012.   }
  1013.   mm_nocritical (stream);    /* release critical */
  1014.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  1015. }
  1016.  
  1017. /* Tenex mail expunge mailbox
  1018.  * Accepts: MAIL stream
  1019.  */
  1020.  
  1021. void tenex_expunge (stream)
  1022.     MAILSTREAM *stream;
  1023. {
  1024.   struct stat sbuf;
  1025.   off_t pos = 0;
  1026.   int ld;
  1027.   unsigned long i = 1;
  1028.   unsigned long j,k,m,recent;
  1029.   unsigned long n = 0;
  1030.   unsigned long delta = 0;
  1031.   char lock[MAILTMPLEN];
  1032.   MESSAGECACHE *elt;
  1033.                 /* do nothing if stream dead */
  1034.   if (!tenex_ping (stream)) return;
  1035.   if (stream->rdonly) {        /* won't do on readonly files! */
  1036.     mm_log ("Expunge ignored on readonly mailbox",WARN);
  1037.     return;
  1038.   }
  1039.   if (LOCAL->filetime && !LOCAL->shouldcheck) {
  1040.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  1041.     if (LOCAL->filetime < sbuf.st_mtime) LOCAL->shouldcheck = T;
  1042.   }
  1043.   /* The cretins who designed flock() created a window of vulnerability in
  1044.    * upgrading locks from shared to exclusive or downgrading from exclusive
  1045.    * to shared.  Rather than maintain the lock at shared status at a minimum,
  1046.    * flock() actually *releases* the former lock.  Obviously they never talked
  1047.    * to any database guys.  Fortunately, we have the parse/append permission
  1048.    * lock.  If we require this lock before going exclusive on the mailbox,
  1049.    * another process can not sneak in and steal the exclusive mailbox lock on
  1050.    * us, because it will block on trying to get parse/append permission first.
  1051.    */
  1052.                 /* get exclusive parse/append permission */
  1053.   if ((ld = tenex_lock (LOCAL->fd,lock,LOCK_EX)) < 0) {
  1054.     mm_log ("Unable to lock expunge mailbox",ERROR);
  1055.     return;
  1056.   }
  1057.                 /* get exclusive access */
  1058.   if (flock (LOCAL->fd,LOCK_EX|LOCK_NB)) {
  1059.     flock (LOCAL->fd,LOCK_SH);    /* recover previous lock */
  1060.     mm_log("Can't expunge because mailbox is in use by another process",ERROR);
  1061.     tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  1062.     return;
  1063.   }
  1064.   tenex_gc (stream,GC_TEXTS);    /* flush texts */
  1065.  
  1066.   mm_critical (stream);        /* go critical */
  1067.   recent = stream->recent;    /* get recent now that pinged and locked */
  1068.   while (i <= stream->nmsgs) {    /* for each message */
  1069.                 /* number of bytes to smash or preserve */
  1070.     k = ((elt = tenex_elt (stream,i))->data2 >> 24) + tenex_size (stream,i);
  1071.     if (elt->deleted) {        /* if deleted */
  1072.       if (elt->recent) --recent;/* if recent, note one less recent message */
  1073.       delta += k;        /* number of bytes to delete */
  1074.       mail_expunged (stream,i);    /* notify upper levels */
  1075.       n++;            /* count up one more expunged message */
  1076.     }
  1077.     else if (i++ && delta) {    /* preserved message */
  1078.       j = elt->data1;        /* first byte to preserve */
  1079.       do {            /* read from source position */
  1080.     m = min (k,LOCAL->buflen);
  1081.     lseek (LOCAL->fd,j,L_SET);
  1082.     read (LOCAL->fd,LOCAL->buf,(unsigned int) m);
  1083.     pos = j - delta;    /* write to destination position */
  1084.     lseek (LOCAL->fd,pos,L_SET);
  1085.     write (LOCAL->fd,LOCAL->buf,(unsigned int) m);
  1086.     pos += m;        /* new position */
  1087.     j += m;            /* next chunk, perhaps */
  1088.       } while (k -= m);        /* until done */
  1089.       elt->data1 -= delta;    /* note the new address of this text */
  1090.     }
  1091.     else pos = elt->data1 + k;    /* preserved but no deleted messages */
  1092.   }
  1093.   if (n) {            /* truncate file after last message */
  1094.     if (pos != (LOCAL->filesize -= delta)) {
  1095.       sprintf (LOCAL->buf,"Calculated size mismatch %ld != %ld, delta = %ld",
  1096.            pos,LOCAL->filesize,delta);
  1097.       mm_log (LOCAL->buf,WARN);
  1098.       LOCAL->filesize = pos;    /* fix it then */
  1099.     }
  1100.     ftruncate (LOCAL->fd,LOCAL->filesize);
  1101.     sprintf (LOCAL->buf,"Expunged %ld messages",n);
  1102.                 /* output the news */
  1103.     mm_log (LOCAL->buf,(long) NIL);
  1104.   }
  1105.   else mm_log ("No messages deleted, so no update needed",(long) NIL);
  1106.   fsync (LOCAL->fd);        /* force disk update */
  1107.   fstat (LOCAL->fd,&sbuf);    /* get new write time */
  1108.   LOCAL->filetime = sbuf.st_mtime;
  1109.   mm_nocritical (stream);    /* release critical */
  1110.                 /* notify upper level of new mailbox size */
  1111.   mail_exists (stream,stream->nmsgs);
  1112.   mail_recent (stream,recent);
  1113.   flock (LOCAL->fd,LOCK_SH);    /* allow sharers again */
  1114.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  1115. }
  1116.  
  1117. /* Tenex mail copy message(s)
  1118.     s;
  1119.  * Accepts: MAIL stream
  1120.  *        sequence
  1121.  *        destination mailbox
  1122.  * Returns: T if success, NIL if failed
  1123.  */
  1124.  
  1125. long tenex_copy (stream,sequence,mailbox)
  1126.     MAILSTREAM *stream;
  1127.     char *sequence;
  1128.     char *mailbox;
  1129. {
  1130.                 /* copy the messages */
  1131.   return (mail_sequence (stream,sequence)) ?
  1132.     tenex_copy_messages (stream,mailbox) : NIL;
  1133. }
  1134.  
  1135.  
  1136. /* Tenex mail move message(s)
  1137.     s;
  1138.  * Accepts: MAIL stream
  1139.  *        sequence
  1140.  *        destination mailbox
  1141.  * Returns: T if success, NIL if failed
  1142.  */
  1143.  
  1144. long tenex_move (stream,sequence,mailbox)
  1145.     MAILSTREAM *stream;
  1146.     char *sequence;
  1147.     char *mailbox;
  1148. {
  1149.   long i;
  1150.   struct stat sbuf;
  1151.   MESSAGECACHE *elt;
  1152.   if (!(mail_sequence (stream,sequence) &&
  1153.     tenex_copy_messages (stream,mailbox))) return NIL;
  1154.                 /* delete all requested messages */
  1155.   for (i = 1; i <= stream->nmsgs; i++)
  1156.     if ((elt = tenex_elt (stream,i))->sequence) {
  1157.       elt->deleted = T;        /* mark message deleted */
  1158.                 /* recalculate status */
  1159.       tenex_update_status (stream,i,NIL);
  1160.     }
  1161.   if (!stream->rdonly) {    /* make sure the update takes */
  1162.     fsync (LOCAL->fd);
  1163.     fstat (LOCAL->fd,&sbuf);    /* get current write time */
  1164.     LOCAL->filetime = sbuf.st_mtime;
  1165.   }
  1166.   return LONGT;
  1167. }
  1168.  
  1169. /* Tenex mail append message from stringstruct
  1170.  * Accepts: MAIL stream
  1171.  *        destination mailbox
  1172.  *        stringstruct of messages to append
  1173.  * Returns: T if append successful, else NIL
  1174.  */
  1175.  
  1176. long tenex_append (stream,mailbox,flags,date,message)
  1177.     MAILSTREAM *stream;
  1178.     char *mailbox;
  1179.     char *flags;
  1180.     char *date;
  1181.                 STRING *message;
  1182. {
  1183.   struct stat sbuf;
  1184.   int fd,ld;
  1185.   char c,*s,tmp[MAILTMPLEN],file[MAILTMPLEN],lock[MAILTMPLEN];
  1186.   time_t tp[2];
  1187.   MESSAGECACHE elt;
  1188.   long i = SIZE (message);
  1189.   long size = 0;
  1190.   long ret = LONGT;
  1191.   unsigned long uf = 0;
  1192.   short f = 0;
  1193.   if (flags) {            /* get flags if given */
  1194.     unsigned long ruf;
  1195.     f = tenex_getflags (user_flags (&tenexproto),flags,&ruf);
  1196.                 /* reverse bits (dontcha wish we had CIRC?) */
  1197.     while (ruf) uf |= 1 << (29 - find_rightmost_bit (&ruf));
  1198.   }
  1199.   if (date) {            /* want to preserve date? */
  1200.                 /* yes, parse date into an elt */
  1201.     if (!mail_parse_date (&elt,date)) {
  1202.       sprintf (tmp,"Bad date in append: %s",date);
  1203.       mm_log (tmp,ERROR);
  1204.       return NIL;
  1205.     }
  1206.   }
  1207.                 /* N.B.: can't use LOCAL->buf for tmp */
  1208.                 /* make sure valid mailbox */
  1209.   if (!tenex_isvalid (mailbox,tmp)) switch (errno) {
  1210.   case ENOENT:            /* no such file? */
  1211.     mm_notify (stream,"[TRYCREATE] Must create mailbox before append",NIL);
  1212.     return NIL;
  1213.   case 0:            /* merely empty file? */
  1214.     break;
  1215.   case EINVAL:
  1216.     sprintf (tmp,"Invalid Tenex-format mailbox name: %s",mailbox);
  1217.     mm_log (tmp,ERROR);
  1218.     return NIL;
  1219.   default:
  1220.     sprintf (tmp,"Not a Tenex-format mailbox: %s",mailbox);
  1221.     mm_log (tmp,ERROR);
  1222.     return NIL;
  1223.   }
  1224.   if ((fd = open (tenex_file (file,mailbox),O_RDWR|O_CREAT,
  1225.           S_IREAD|S_IWRITE)) < 0) {
  1226.     sprintf (tmp,"Can't open append mailbox: %s",strerror (errno));
  1227.     mm_log (tmp,ERROR);
  1228.     return NIL;
  1229.   }
  1230.                 /* get exclusive parse/append permission */
  1231.   if ((ld = tenex_lock (fd,lock,LOCK_EX)) < 0) {
  1232.     mm_log ("Unable to lock append mailbox",ERROR);
  1233.     return NIL;
  1234.   }
  1235.   s = (char *) fs_get (i + 1);    /* get space for the data */
  1236.                 /* copy the data w/o CR's */
  1237.   while (i--) if ((c = SNX (message)) != '\015') s[size++] = c;
  1238.  
  1239.   mm_critical (stream);        /* go critical */
  1240.   fstat (fd,&sbuf);        /* get current file size */
  1241.   lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
  1242.                 /* write preseved date */
  1243.   if (date) mail_date (tmp,&elt);
  1244.   else internal_date (tmp);    /* get current date in IMAP format */
  1245.                 /* add remainder of header */
  1246.   sprintf (tmp+26,",%ld;%010lo%02o\n",size,uf,f);
  1247.                 /* write header */
  1248.   if ((write (fd,tmp,strlen (tmp)) < 0) || ((write (fd,s,size)) < 0) ||
  1249.       fsync (fd)) {
  1250.     sprintf (tmp,"Message append failed: %s",strerror (errno));
  1251.     mm_log (tmp,ERROR);
  1252.     ftruncate (fd,sbuf.st_size);
  1253.     ret = NIL;
  1254.   }
  1255.   tp[0] = sbuf.st_atime;    /* preserve atime and mtime */
  1256.   tp[1] = sbuf.st_mtime;
  1257.   utime (file,tp);        /* set the times */
  1258.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  1259.   close (fd);            /* close the file */
  1260.   mm_nocritical (stream);    /* release critical */
  1261.   fs_give ((void **) &s);    /* flush the buffer */
  1262.   return ret;
  1263. }
  1264.  
  1265. /* Tenex garbage collect stream
  1266.  * Accepts: Mail stream
  1267.  *        garbage collection flags
  1268.  */
  1269.  
  1270. void tenex_gc (stream,gcflags)
  1271.     MAILSTREAM *stream;
  1272.     long gcflags;
  1273. {
  1274.   if (gcflags & GC_TEXTS) {    /* flush texts */
  1275.     LOCAL->hdrmsgno = LOCAL->txtmsgno = 0;
  1276.     if (LOCAL->hdr) fs_give ((void **) &LOCAL->hdr);
  1277.     if (LOCAL->txt) fs_give ((void **) &LOCAL->txt);
  1278.   }
  1279. }
  1280.  
  1281. /* Internal routines */
  1282.  
  1283.  
  1284. /* Tenex mail lock file for parse/append permission
  1285.  * Accepts: file descriptor
  1286.  *        lock file name buffer
  1287.  *        type of locking operation (LOCK_SH or LOCK_EX)
  1288.  * Returns: file descriptor of lock or -1 if failure
  1289.  */
  1290.  
  1291. int tenex_lock (fd,lock,op)
  1292.     int fd;
  1293.     char *lock;
  1294.     int op;
  1295. {
  1296.   int ld;
  1297.   struct stat sbuf;
  1298.   if (!fstat (fd,&sbuf)) {    /* get data for this file */
  1299.                 /* make temporary file name */
  1300.     sprintf (lock,"/tmp/.%hx.%lx",sbuf.st_dev,sbuf.st_ino);
  1301.     if (chk_notsymlink (lock,T) &&
  1302.     ((ld = open (lock,O_RDWR|O_CREAT,
  1303.              (int)mail_parameters(NIL,GET_LOCKPROTECTION,NIL))) >= 0)){
  1304.       flock (ld,op);        /* get this lock */
  1305.       return ld;        /* return locking file descriptor */
  1306.     }
  1307.   }
  1308.   return -1;
  1309. }
  1310.  
  1311.  
  1312. /* Tenex mail unlock file for parse/append permission
  1313.  * Accepts: file descriptor
  1314.  *        lock file name from tenex_lock()
  1315.  */
  1316.  
  1317. void tenex_unlock (fd,lock)
  1318.     int fd;
  1319.     char *lock;
  1320. {
  1321.   unlink (lock);        /* delete the file */
  1322.   flock (fd,LOCK_UN);        /* unlock it */
  1323.   close (fd);            /* close it */
  1324. }
  1325.  
  1326. /* Tenex mail return internal message size in bytes
  1327.  * Accepts: MAIL stream
  1328.  *        message #
  1329.  * Returns: internal size of message
  1330.  */
  1331.  
  1332. unsigned long tenex_size (stream,m)
  1333.     MAILSTREAM *stream;
  1334.     long m;
  1335. {
  1336.   MESSAGECACHE *elt = mail_elt (stream,m);
  1337.   return ((m < stream->nmsgs) ? mail_elt (stream,m+1)->data1 : LOCAL->filesize)
  1338.     - (elt->data1 + (elt->data2 >> 24));
  1339. }
  1340.  
  1341.  
  1342. /* Tenex mail return RFC-822 size in bytes
  1343.  * Accepts: MAIL stream
  1344.  *        message #
  1345.  * Returns: message size
  1346.  */
  1347.  
  1348. unsigned long tenex_822size (stream,msgno)
  1349.     MAILSTREAM *stream;
  1350.     long msgno;
  1351. {
  1352.   char *s;
  1353.   unsigned long i;
  1354.   STRING bs;
  1355.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1356.   if (!elt->rfc822_size) {    /* have header size yet? */
  1357.                 /* get size including CR's  */
  1358.     s = tenex_fetchheader_work (stream,msgno,&i);
  1359.     INIT (&bs,mail_string,(void *) s,i);
  1360.     elt->rfc822_size = strcrlflen (&bs);
  1361.     s = tenex_fetchtext_work (stream,msgno,&i);
  1362.     INIT (&bs,mail_string,(void *) s,i);
  1363.     elt->rfc822_size += strcrlflen (&bs);
  1364.   }
  1365.   return elt->rfc822_size;
  1366. }
  1367.  
  1368.  
  1369. /* Tenex mail generate file string
  1370.  * Accepts: temporary buffer to write into
  1371.  *        mailbox name string
  1372.  * Returns: local file string or NIL if failure
  1373.  */
  1374.  
  1375. char *tenex_file (dst,name)
  1376.     char *dst;
  1377.     char *name;
  1378. {
  1379.   char tmp[MAILTMPLEN];
  1380.   char *s = mailboxfile (dst,name);
  1381.                 /* return our standard inbox */
  1382.   return (s && !*s) ? mailboxfile (dst,tenex_isvalid ("~/mail.TxT",tmp) ?
  1383.                    "mail.TxT" : "mail.txt") : s;
  1384. }
  1385.  
  1386. /* Parse flag list
  1387.  * Accepts: MAIL stream
  1388.  *        flag list as a character string
  1389.  *        pointer to user flags to return
  1390.  * Returns: system flags
  1391.  */
  1392.  
  1393. long tenex_getflags (stream,flag,uf)
  1394.     MAILSTREAM *stream;
  1395.     char *flag;
  1396.     unsigned long *uf;
  1397. {
  1398.   char key[MAILTMPLEN];
  1399.   char *t,*s,tmp[MAILTMPLEN];
  1400.   short f = 0;
  1401.   long i;
  1402.   short j;
  1403.   *uf = 0;            /* initially no user flags */
  1404.   if (flag && *flag) {        /* no-op if no flag string */
  1405.                 /* check if a list and make sure valid */
  1406.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  1407.       mm_log ("Bad flag list",ERROR);
  1408.       return NIL;
  1409.     }
  1410.                 /* copy the flag string w/o list construct */
  1411.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  1412.     tmp[j] = '\0';        /* tie off tail */
  1413.                 /* make uppercase, find first, parse */
  1414.     if (t = strtok (ucase (tmp)," ")) do {
  1415.       i = 0;            /* no flag yet */
  1416.                 /* system flag, dispatch on first character */
  1417.       if (*t == '\\') switch (*++t) {
  1418.       case 'S':            /* possible \Seen flag */
  1419.     if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N' && t[4] == '\0')
  1420.       f |= i = fSEEN;
  1421.     break;
  1422.       case 'D':            /* possible \Deleted flag */
  1423.     if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  1424.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fDELETED;
  1425.     break;
  1426.       case 'F':            /* possible \Flagged flag */
  1427.     if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  1428.         t[5] == 'E' && t[6] == 'D' && t[7] == '\0') f |= i = fFLAGGED;
  1429.     break;
  1430.       case 'A':            /* possible \Answered flag */
  1431.     if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  1432.         t[5] == 'R' && t[6] == 'E' && t[7] == 'D' && t[8] == '\0')
  1433.       f |= i = fANSWERED;
  1434.     break;
  1435.       default:            /* unknown */
  1436.     break;
  1437.       }
  1438.  
  1439.                 /* user flag, search through table */
  1440.       else for (j = 0; !i && j < NUSERFLAGS && (s =stream->user_flags[j]); ++j)
  1441.     if (!strcmp (t,ucase (strcpy (key,s)))) *uf |= i = 1 << j;
  1442.       if (!i) {            /* didn't find a matching flag? */
  1443.     sprintf (key,"Unknown flag: %.80s",t);
  1444.     mm_log (key,ERROR);
  1445.       }
  1446.                 /* parse next flag */
  1447.     } while (t = strtok (NIL," "));
  1448.   }
  1449.   return f;
  1450. }
  1451.  
  1452. /* Tenex mail parse mailbox
  1453.  * Accepts: MAIL stream
  1454.  * Returns: T if parse OK
  1455.  *        NIL if failure, stream aborted
  1456.  */
  1457.  
  1458. long tenex_parse (stream)
  1459.     MAILSTREAM *stream;
  1460. {
  1461.   struct stat sbuf;
  1462.   MESSAGECACHE *elt = NIL;
  1463.   char c,*s,*t,*x;
  1464.   char tmp[MAILTMPLEN];
  1465.   unsigned long i,j,msiz;
  1466.   long curpos = LOCAL->filesize;
  1467.   long nmsgs = stream->nmsgs;
  1468.   long recent = stream->recent;
  1469.   fstat (LOCAL->fd,&sbuf);    /* get status */
  1470.   if (sbuf.st_size < curpos) {    /* sanity check */
  1471.     sprintf (tmp,"Mailbox shrank from %ld to %ld!",curpos,sbuf.st_size);
  1472.     mm_log (tmp,ERROR);
  1473.     tenex_close (stream);
  1474.     return NIL;
  1475.   }
  1476.   while (sbuf.st_size - curpos){/* while there is stuff to parse */
  1477.                 /* get to that position in the file */
  1478.     lseek (LOCAL->fd,curpos,L_SET);
  1479.     if ((i = read (LOCAL->fd,LOCAL->buf,64)) <= 0) {
  1480.       sprintf (tmp,"Unable to read internal header at %ld, size = %ld: %s",
  1481.            curpos,sbuf.st_size,i ? strerror (errno) : "no data read");
  1482.       mm_log (tmp,ERROR);
  1483.       tenex_close (stream);
  1484.       return NIL;
  1485.     }
  1486.     LOCAL->buf[i] = '\0';    /* tie off buffer just in case */
  1487.     if (!(s = strchr (LOCAL->buf,'\012'))) {
  1488.       sprintf (tmp,"Unable to find newline at %ld in %ld bytes, text: %s",
  1489.            curpos,i,LOCAL->buf);
  1490.       mm_log (tmp,ERROR);
  1491.       tenex_close (stream);
  1492.       return NIL;
  1493.     }
  1494.     *s = '\0';            /* tie off header line */
  1495.     i = (s + 1) - LOCAL->buf;    /* note start of text offset */
  1496.     if (!((s = strchr (LOCAL->buf,',')) && (t = strchr (s+1,';')))) {
  1497.       sprintf (tmp,"Unable to parse internal header at %ld: %s",curpos,
  1498.            LOCAL->buf);
  1499.       mm_log (tmp,ERROR);
  1500.       tenex_close (stream);
  1501.       return NIL;
  1502.     }
  1503.     *s++ = '\0'; *t++ = '\0';    /* tie off fields */
  1504.  
  1505.                 /* intantiate an elt for this message */
  1506.     (elt = mail_elt (stream,++nmsgs))->valid = T;
  1507.     elt->data1 = curpos;    /* note file offset of header */
  1508.     elt->data2 = i << 24;    /* as well as offset from header of message */
  1509.                 /* parse the header components */
  1510.     if (!(mail_parse_date (elt,LOCAL->buf) &&
  1511.       (msiz = strtol (x = s,&s,10)) && (!(s && *s)) &&
  1512.       isdigit (t[0]) && isdigit (t[1]) && isdigit (t[2]) &&
  1513.       isdigit (t[3]) && isdigit (t[4]) && isdigit (t[5]) &&
  1514.       isdigit (t[6]) && isdigit (t[7]) && isdigit (t[8]) &&
  1515.       isdigit (t[9]) && isdigit (t[10]) && isdigit (t[11]) && !t[12])) {
  1516.       sprintf (tmp,"Unable to parse internal header elements at %ld: %s,%s;%s",
  1517.            curpos,LOCAL->buf,x,t);
  1518.       mm_log (tmp,ERROR);
  1519.       tenex_close (stream);
  1520.       return NIL;
  1521.     }
  1522.                 /* make sure didn't run off end of file */
  1523.     if ((curpos += (msiz + i)) > sbuf.st_size) {
  1524.       mm_log ("Last message runs past end of file",ERROR);
  1525.       tenex_close (stream);
  1526.       return NIL;
  1527.     }
  1528.     c = t[10];            /* remember first system flags byte */
  1529.     t[10] = '\0';        /* tie off flags */
  1530.     j = strtol (t,NIL,8);    /* get user flags value */
  1531.     t[10] = c;            /* restore first system flags byte */
  1532.                 /* set up all valid user flags (reversed!) */
  1533.     while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
  1534.           stream->user_flags[i]) elt->user_flags |= 1 << i;
  1535.                 /* calculate system flags */
  1536.     if ((j = ((t[10]-'0') * 8) + t[11]-'0') & fSEEN) elt->seen = T;
  1537.     if (j & fDELETED) elt->deleted = T;
  1538.     if (j & fFLAGGED) elt->flagged = T;
  1539.     if (j & fANSWERED) elt->answered = T;
  1540.     if (!(j & fOLD)) {        /* newly arrived message? */
  1541.       elt->recent = T;
  1542.       recent++;            /* count up a new recent message */
  1543.                 /* mark it as old */
  1544.       tenex_update_status (stream,nmsgs,NIL);
  1545.     }
  1546.   }
  1547.   fsync (LOCAL->fd);        /* make sure all the fOLD flags take */
  1548.                 /* update parsed file size and time */
  1549.   LOCAL->filesize = sbuf.st_size;
  1550.   fstat (LOCAL->fd,&sbuf);    /* get status again to ensure time is right */
  1551.   LOCAL->filetime = sbuf.st_mtime;
  1552.   mail_exists (stream,nmsgs);    /* notify upper level of new mailbox size */
  1553.   mail_recent (stream,recent);    /* and of change in recent messages */
  1554.   return LONGT;            /* return the winnage */
  1555. }
  1556.  
  1557. /* Tenex copy messages
  1558.  * Accepts: MAIL stream
  1559.  *        mailbox copy vector
  1560.  *        mailbox name
  1561.  * Returns: T if success, NIL if failed
  1562.  */
  1563.  
  1564. long tenex_copy_messages (stream,mailbox)
  1565.     MAILSTREAM *stream;
  1566.     char *mailbox;
  1567. {
  1568.   struct stat sbuf;
  1569.   time_t tp[2];
  1570.   MESSAGECACHE *elt;
  1571.   unsigned long i,j,k;
  1572.   long ret = LONGT;
  1573.   int fd,ld;
  1574.   char file[MAILTMPLEN],lock[MAILTMPLEN];
  1575.                 /* make sure valid mailbox */
  1576.   if (!tenex_isvalid (mailbox,LOCAL->buf)) switch (errno) {
  1577.   case ENOENT:            /* no such file? */
  1578.     mm_notify (stream,"[TRYCREATE] Must create mailbox before copy",NIL);
  1579.     return NIL;
  1580.   case 0:            /* merely empty file? */
  1581.     break;
  1582.   case EINVAL:
  1583.     sprintf (LOCAL->buf,"Invalid Tenex-format mailbox name: %s",mailbox);
  1584.     mm_log (LOCAL->buf,ERROR);
  1585.     return NIL;
  1586.   default:
  1587.     sprintf (LOCAL->buf,"Not a Tenex-format mailbox: %s",mailbox);
  1588.     mm_log (LOCAL->buf,ERROR);
  1589.     return NIL;
  1590.   }
  1591.                 /* got file? */
  1592.   if ((fd=open(tenex_file(file,mailbox),O_RDWR|O_CREAT,S_IREAD|S_IWRITE))<0) {
  1593.     sprintf (LOCAL->buf,"Unable to open copy mailbox: %s",strerror (errno));
  1594.     mm_log (LOCAL->buf,ERROR);
  1595.     return NIL;
  1596.   }
  1597.   mm_critical (stream);        /* go critical */
  1598.                 /* get exclusive parse/append permission */
  1599.   if ((ld = tenex_lock (fd,lock,LOCK_EX)) < 0) {
  1600.     mm_log ("Unable to lock copy mailbox",ERROR);
  1601.     return NIL;
  1602.   }
  1603.   fstat (fd,&sbuf);        /* get current file size */
  1604.   lseek (fd,sbuf.st_size,L_SET);/* move to end of file */
  1605.  
  1606.                 /* for each requested message */
  1607.   for (i = 1; ret && (i <= stream->nmsgs); i++)
  1608.     if ((elt = mail_elt (stream,i))->sequence) {
  1609.       lseek (LOCAL->fd,elt->data1,L_SET);
  1610.                 /* number of bytes to copy */
  1611.       k = (elt->data2 >> 24) + tenex_size (stream,i);
  1612.       do {            /* read from source position */
  1613.     j = min (k,LOCAL->buflen);
  1614.     read (LOCAL->fd,LOCAL->buf,(unsigned int) j);
  1615.     if ((write (fd,LOCAL->buf,(unsigned int) j) < 0) || fsync (fd)) {
  1616.       sprintf (LOCAL->buf,"Unable to write message: %s",strerror (errno));
  1617.       mm_log (LOCAL->buf,ERROR);
  1618.       ftruncate (fd,sbuf.st_size);
  1619.       j = k;
  1620.       ret = NIL;        /* note error */
  1621.       break;
  1622.     }
  1623.       } while (k -= j);        /* until done */
  1624.     }
  1625.   tp[0] = sbuf.st_atime;    /* preserve atime and mtime */
  1626.   tp[1] = sbuf.st_mtime;
  1627.   utime (file,tp);        /* set the times */
  1628.   tenex_unlock (ld,lock);    /* release exclusive parse/append permission */
  1629.   close (fd);            /* close the file */
  1630.   mm_nocritical (stream);    /* release critical */
  1631.   return ret;
  1632. }
  1633.  
  1634. /* Tenex get cache element with status updating from file
  1635.  * Accepts: MAIL stream
  1636.  *        message number
  1637.  * Returns: cache element
  1638.  */
  1639.  
  1640. MESSAGECACHE *tenex_elt (stream,msgno)
  1641.     MAILSTREAM *stream;
  1642.     long msgno;
  1643. {
  1644.   unsigned long i,j,sysflags;
  1645.   char c;
  1646.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1647.   unsigned long oldsysflags = (elt->seen ? fSEEN : NIL) |
  1648.     (elt->deleted ? fDELETED : NIL) | (elt->flagged ? fFLAGGED : NIL) |
  1649.       (elt->answered ? fANSWERED : NIL);
  1650.   unsigned long olduserflags = elt->user_flags;
  1651.                 /* set the seek pointer */
  1652.   lseek (LOCAL->fd,(off_t) elt->data1 + (elt->data2 >> 24) - 13,L_SET);
  1653.                 /* read the new flags */
  1654.   if (read (LOCAL->fd,LOCAL->buf,12) < 0) {
  1655.     sprintf (LOCAL->buf,"Unable to read new status: %s",strerror (errno));
  1656.     fatal (LOCAL->buf);
  1657.   }
  1658.                 /* calculate system flags */
  1659.   sysflags = (((LOCAL->buf[10]-'0') * 8) + LOCAL->buf[11]-'0') &
  1660.     (fSEEN|fDELETED|fFLAGGED|fANSWERED);
  1661.   elt->seen = sysflags & fSEEN ? T : NIL;
  1662.   elt->deleted = sysflags & fDELETED ? T : NIL;
  1663.   elt->flagged = sysflags & fFLAGGED ? T : NIL;
  1664.   elt->answered = sysflags & fANSWERED ? T : NIL;
  1665.   c = LOCAL->buf[10];        /* remember first system flags byte */
  1666.   LOCAL->buf[10] = '\0';    /* tie off flags */
  1667.   j = strtol (LOCAL->buf,NIL,8);/* get user flags value */
  1668.   LOCAL->buf[10] = c;        /* restore first system flags byte */
  1669.                 /* set up all valid user flags (reversed!) */
  1670.   while (j) if (((i = 29 - find_rightmost_bit (&j)) < NUSERFLAGS) &&
  1671.         stream->user_flags[i]) elt->user_flags |= 1 << i;
  1672.   if ((oldsysflags != sysflags) || (olduserflags != elt->user_flags))
  1673.     mm_flags (stream,msgno);    /* let top level know */
  1674.   return elt;
  1675. }
  1676.  
  1677. /* Tenex update status string
  1678.  * Accepts: MAIL stream
  1679.  *        message number
  1680.  *        flag saying whether or not to sync
  1681.  */
  1682.  
  1683. void tenex_update_status (stream,msgno,syncflag)
  1684.     MAILSTREAM *stream;
  1685.     long msgno;
  1686.     long syncflag;
  1687. {
  1688.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1689.   struct stat sbuf;
  1690.   unsigned long j,k = 0;
  1691.   if (!stream->rdonly) {    /* not if readonly you don't */
  1692.     j = elt->user_flags;    /* get user flags */
  1693.                 /* reverse bits (dontcha wish we had CIRC?) */
  1694.     while (j) k |= 1 << (29 - find_rightmost_bit (&j));
  1695.                 /* print new flag string */
  1696.     sprintf (LOCAL->buf,"%010lo%02o",k,
  1697.          fOLD + (fSEEN * elt->seen) + (fDELETED * elt->deleted) +
  1698.          (fFLAGGED * elt->flagged) + (fANSWERED * elt->answered));
  1699.                 /* get to that place in the file */
  1700.     lseek (LOCAL->fd,(off_t) elt->data1 + (elt->data2 >> 24) - 13,L_SET);
  1701.                 /* write new flags */
  1702.     write (LOCAL->fd,LOCAL->buf,12);
  1703.                 /* sync if requested */
  1704.     if (syncflag) fsync (LOCAL->fd);
  1705.     fstat (LOCAL->fd,&sbuf);    /* get new write time */
  1706.     LOCAL->filetime = sbuf.st_mtime;
  1707.   }
  1708. }
  1709.  
  1710. /* Search support routines
  1711.  * Accepts: MAIL stream
  1712.  *        message number
  1713.  *        pointer to additional data
  1714.  * Returns: T if search matches, else NIL
  1715.  */
  1716.  
  1717.  
  1718. char tenex_search_all (stream,msgno,d,n)
  1719.     MAILSTREAM *stream;
  1720.     long msgno;
  1721.     char *d;
  1722.     long n;
  1723. {
  1724.   return T;            /* ALL always succeeds */
  1725. }
  1726.  
  1727.  
  1728. char tenex_search_answered (stream,msgno,d,n)
  1729.     MAILSTREAM *stream;
  1730.     long msgno;
  1731.     char *d;
  1732.     long n;
  1733. {
  1734.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1735. }
  1736.  
  1737.  
  1738. char tenex_search_deleted (stream,msgno,d,n)
  1739.     MAILSTREAM *stream;
  1740.     long msgno;
  1741.     char *d;
  1742.     long n;
  1743. {
  1744.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1745. }
  1746.  
  1747.  
  1748. char tenex_search_flagged (stream,msgno,d,n)
  1749.     MAILSTREAM *stream;
  1750.     long msgno;
  1751.     char *d;
  1752.     long n;
  1753. {
  1754.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1755. }
  1756.  
  1757.  
  1758. char tenex_search_keyword (stream,msgno,d,n)
  1759.     MAILSTREAM *stream;
  1760.     long msgno;
  1761.     char *d;
  1762.     long n;
  1763. {
  1764.   return mail_elt (stream,msgno)->user_flags & n ? T : NIL;
  1765. }
  1766.  
  1767.  
  1768. char tenex_search_new (stream,msgno,d,n)
  1769.     MAILSTREAM *stream;
  1770.     long msgno;
  1771.     char *d;
  1772.     long n;
  1773. {
  1774.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1775.   return (elt->recent && !elt->seen) ? T : NIL;
  1776. }
  1777.  
  1778. char tenex_search_old (stream,msgno,d,n)
  1779.     MAILSTREAM *stream;
  1780.     long msgno;
  1781.     char *d;
  1782.     long n;
  1783. {
  1784.   return mail_elt (stream,msgno)->recent ? NIL : T;
  1785. }
  1786.  
  1787.  
  1788. char tenex_search_recent (stream,msgno,d,n)
  1789.     MAILSTREAM *stream;
  1790.     long msgno;
  1791.     char *d;
  1792.     long n;
  1793. {
  1794.   return mail_elt (stream,msgno)->recent ? T : NIL;
  1795. }
  1796.  
  1797.  
  1798. char tenex_search_seen (stream,msgno,d,n)
  1799.     MAILSTREAM *stream;
  1800.     long msgno;
  1801.     char *d;
  1802.     long n;
  1803. {
  1804.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1805. }
  1806.  
  1807.  
  1808. char tenex_search_unanswered (stream,msgno,d,n)
  1809.     MAILSTREAM *stream;
  1810.     long msgno;
  1811.     char *d;
  1812.     long n;
  1813. {
  1814.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1815. }
  1816.  
  1817.  
  1818. char tenex_search_undeleted (stream,msgno,d,n)
  1819.     MAILSTREAM *stream;
  1820.     long msgno;
  1821.     char *d;
  1822.     long n;
  1823. {
  1824.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1825. }
  1826.  
  1827.  
  1828. char tenex_search_unflagged (stream,msgno,d,n)
  1829.     MAILSTREAM *stream;
  1830.     long msgno;
  1831.     char *d;
  1832.     long n;
  1833. {
  1834.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1835. }
  1836.  
  1837.  
  1838. char tenex_search_unkeyword (stream,msgno,d,n)
  1839.     MAILSTREAM *stream;
  1840.     long msgno;
  1841.     char *d;
  1842.     long n;
  1843. {
  1844.   return mail_elt (stream,msgno)->user_flags & n ? NIL : T;
  1845. }
  1846.  
  1847.  
  1848. char tenex_search_unseen (stream,msgno,d,n)
  1849.     MAILSTREAM *stream;
  1850.     long msgno;
  1851.     char *d;
  1852.     long n;
  1853. {
  1854.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1855. }
  1856.  
  1857. char tenex_search_before (stream,msgno,d,n)
  1858.     MAILSTREAM *stream;
  1859.     long msgno;
  1860.     char *d;
  1861.     long n;
  1862. {
  1863.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1864.   return (char) ((long)((elt->year << 9) + (elt->month << 5) + elt->day) < n);
  1865. }
  1866.  
  1867.  
  1868. char tenex_search_on (stream,msgno,d,n)
  1869.     MAILSTREAM *stream;
  1870.     long msgno;
  1871.     char *d;
  1872.     long n;
  1873. {
  1874.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1875.   return (char) (((elt->year << 9) + (elt->month << 5) + elt->day) == n);
  1876. }
  1877.  
  1878.  
  1879. char tenex_search_since (stream,msgno,d,n)
  1880.     MAILSTREAM *stream;
  1881.     long msgno;
  1882.     char *d;
  1883.     long n;
  1884. {
  1885.                 /* everybody interprets "since" as .GE. */
  1886.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1887.   return (char) ((long)((elt->year << 9) + (elt->month << 5) + elt->day) >= n);
  1888. }
  1889.  
  1890.  
  1891. char tenex_search_body (stream,msgno,d,n)
  1892.     MAILSTREAM *stream;
  1893.     long msgno;
  1894.     char *d;
  1895.     long n;
  1896. {
  1897.   unsigned long textsize;
  1898.   char *s = tenex_fetchtext_work (stream,msgno,&textsize);
  1899.                 /* recalculate status */
  1900.   tenex_update_status (stream,msgno,T);
  1901.   return search (s,textsize,d,n);
  1902. }
  1903.  
  1904.  
  1905. char tenex_search_subject (stream,msgno,d,n)
  1906.     MAILSTREAM *stream;
  1907.     long msgno;
  1908.     char *d;
  1909.     long n;
  1910. {
  1911.   char *s = tenex_fetchstructure (stream,msgno,NIL)->subject;
  1912.   return s ? search (s,(long) strlen (s),d,n) : NIL;
  1913. }
  1914.  
  1915.  
  1916. char tenex_search_text (stream,msgno,d,n)
  1917.     MAILSTREAM *stream;
  1918.     long msgno;
  1919.     char *d;
  1920.     long n;
  1921. {
  1922.   unsigned long hdrsize;
  1923.   char *s = tenex_fetchheader_work (stream,msgno,&hdrsize);
  1924.   return search (s,hdrsize,d,n) || tenex_search_body (stream,msgno,d,n);
  1925. }
  1926.  
  1927. char tenex_search_bcc (stream,msgno,d,n)
  1928.     MAILSTREAM *stream;
  1929.     long msgno;
  1930.     char *d;
  1931.     long n;
  1932. {
  1933.   ADDRESS *a = tenex_fetchstructure (stream,msgno,NIL)->bcc;
  1934.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1935.                 /* get text for address */
  1936.   rfc822_write_address (LOCAL->buf,a);
  1937.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1938. }
  1939.  
  1940.  
  1941. char tenex_search_cc (stream,msgno,d,n)
  1942.     MAILSTREAM *stream;
  1943.     long msgno;
  1944.     char *d;
  1945.     long n;
  1946. {
  1947.   ADDRESS *a = tenex_fetchstructure (stream,msgno,NIL)->cc;
  1948.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1949.                 /* get text for address */
  1950.   rfc822_write_address (LOCAL->buf,a);
  1951.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1952. }
  1953.  
  1954.  
  1955. char tenex_search_from (stream,msgno,d,n)
  1956.     MAILSTREAM *stream;
  1957.     long msgno;
  1958.     char *d;
  1959.     long n;
  1960. {
  1961.   ADDRESS *a = tenex_fetchstructure (stream,msgno,NIL)->from;
  1962.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1963.                 /* get text for address */
  1964.   rfc822_write_address (LOCAL->buf,a);
  1965.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1966. }
  1967.  
  1968.  
  1969. char tenex_search_to (stream,msgno,d,n)
  1970.     MAILSTREAM *stream;
  1971.     long msgno;
  1972.     char *d;
  1973.     long n;
  1974. {
  1975.   ADDRESS *a = tenex_fetchstructure (stream,msgno,NIL)->to;
  1976.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1977.                 /* get text for address */
  1978.   rfc822_write_address (LOCAL->buf,a);
  1979.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1980. }
  1981.  
  1982. /* Search parsers */
  1983.  
  1984.  
  1985. /* Parse a date
  1986.  * Accepts: function to return
  1987.  *        pointer to date integer to return
  1988.  * Returns: function to return
  1989.  */
  1990.  
  1991. search_t tenex_search_date (f,n)
  1992.     search_t f;
  1993.     long *n;
  1994. {
  1995.   long i;
  1996.   char *s;
  1997.   MESSAGECACHE elt;
  1998.                 /* parse the date and return fn if OK */
  1999.   return (tenex_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  2000.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  2001. }
  2002.  
  2003. /* Parse a flag
  2004.  * Accepts: function to return
  2005.  *        pointer to keyword integer to return
  2006.  *        MAIL stream
  2007.  * Returns: function to return
  2008.  */
  2009.  
  2010. search_t tenex_search_flag (f,n,stream)
  2011.     search_t f;
  2012.     long *n;
  2013.     MAILSTREAM *stream;
  2014. {
  2015.   short i;
  2016.   char *s,*t;
  2017.   if (t = strtok (NIL," ")) {    /* get a keyword */
  2018.     ucase (t);            /* get uppercase form of flag */
  2019.     for (i = 0; i < NUSERFLAGS && (s = stream->user_flags[i]); ++i)
  2020.       if (!strcmp (t,ucase (strcpy (LOCAL->buf,s))) && (*n = 1 << i)) return f;
  2021.   }
  2022.   return NIL;            /* couldn't find keyword */
  2023. }
  2024.  
  2025. /* Parse a string
  2026.  * Accepts: function to return
  2027.  *        pointer to string to return
  2028.  *        pointer to string length to return
  2029.  * Returns: function to return
  2030.  */
  2031.  
  2032.  
  2033. search_t tenex_search_string (f,d,n)
  2034.     search_t f;
  2035.     char **d;
  2036.     long *n;
  2037. {
  2038.   char *end = " ";
  2039.   char *c = strtok (NIL,"");    /* remainder of criteria */
  2040.   if (!c) return NIL;        /* missing argument */
  2041.   switch (*c) {            /* see what the argument is */
  2042.   case '{':            /* literal string */
  2043.     *n = strtol (c+1,d,10);    /* get its length */
  2044.     if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012') &&
  2045.     (!(*(c = *d + *n)) || (*c == ' '))) {
  2046.       char e = *--c;
  2047.       *c = DELIM;        /* make sure not a space */
  2048.       strtok (c," ");        /* reset the strtok mechanism */
  2049.       *c = e;            /* put character back */
  2050.       break;
  2051.     }
  2052.   case '\0':            /* catch bogons */
  2053.   case ' ':
  2054.     return NIL;
  2055.   case '"':            /* quoted string */
  2056.     if (strchr (c+1,'"')) end = "\"";
  2057.     else return NIL;
  2058.   default:            /* atomic string */
  2059.     if (*d = strtok (c,end)) *n = strlen (*d);
  2060.     else return NIL;
  2061.     break;
  2062.   }
  2063.   return f;
  2064. }
  2065.